<?php
/**
 * Nested Model Tree.
 *
 */
class LeeNT_Tree
{
	/**
	 * LeeNTDbMySql 对象。
	 *
	 * @var LeeNT_MySql
	 */
	private $db = NULL;
	
	/**
	 * 树型表结构定义。
	 *
	 * @var array
	 */
	private $treeStructure = NULL;
	
	/**
	 * 构造函数。 
	 *
	 * @param LeeNT_MySql $db
	 * @param array $tableStructure
	 */
	function __construct(&$db, $tableStructure = NULL)
	{
		$this->db = &$db;
		if (!is_null($tableStructure)) {
			$this->treeStructure = $tableStructure;
		} else {
			$this->treeStructure = array(
				'Table'   => 'ms_classes',
				'Primary' => 'ClassID',
				'Lft'     => 'Lft',
				'Rgt'     => 'Rgt',
				'Level'   => 'Depth'
			);
		}
	}
	
	/**
	 * 设定树型结构。
	 *
	 * @param array $table
	 */
	function setTreeStructure($table)
	{
		$this->treeStructure = $table;
	}
	
	/**
	 * 创建新节点。
	 *
	 * @param int $iParentId
	 * @param array $attachFields
	 * @return int
	 */
	function create($iParentId, $attachFields = NULL)
	{
		$dNodeInfo = $this->getNodeInfo($iParentId);
		if ($dNodeInfo == false)
			return false;
			
		list($iLeftId, $iRightId, $iLevel) = $dNodeInfo;
		
		$dNodeInfo2 = array(
			'Lft'   => $iRightId,
			'Rgt'   => $iRightId + 1,
			'Level' => $iLevel + 1
		);
		
		$this->db->update("UPDATE `" . $this->treeStructure['Table'] . "` SET 
			`" . $this->treeStructure['Lft'] . "` = (CASE WHEN `" . $this->treeStructure['Lft'] . "` >  " . $iRightId . " THEN `" . $this->treeStructure['Lft'] . "` + 2 ELSE `" . $this->treeStructure['Lft'] . "` END), 
			`" . $this->treeStructure['Rgt'] . "` = (CASE WHEN `" . $this->treeStructure['Rgt'] . "` >= " . $iRightId . " THEN `" . $this->treeStructure['Rgt'] . "` + 2 ELSE `" . $this->treeStructure['Rgt'] . "` END)
			WHERE `" . $this->treeStructure['Rgt'] . "` >= " . $iRightId);
		
		$fields = '';
		$values = '';
		$dataParamsInsert = array();
		if (is_array($attachFields)) {
			foreach ($attachFields as $key => $value) {
				$fields .= ", `" . $key . "`";
				$values .= ", ?";
				$dataParamsInsert[] = $value;
			}
		}
		
		$iLastInstId = $this->db->insert("INSERT INTO `" . $this->treeStructure['Table'] . "`(`" . $this->treeStructure['Lft'] . "`, `" . $this->treeStructure['Rgt'] . "`, `" . $this->treeStructure['Level'] . "`" . $fields . ") VALUES(" . $dNodeInfo2['Lft'] . ", " . $dNodeInfo2['Rgt'] . ", " . $dNodeInfo2['Level'] . "" . $values . ")", $dataParamsInsert);
		
		return $iLastInstId;
	}
	
	/**
	 * 删除节点。
	 *
	 * @param int $iSectionId
	 * @param boolean $bRecursion 是否递归删除子节点。
	 * @param string $sCondition
	 * @param array $dataParams
	 * @return boolean
	 */
	function delete($iSectionId, $bRecursion = true, $sCondition = '', $dataParams = NULL)
	{
		$dNodeInfo = $this->getNodeInfo($iSectionId);
		
		if ($dNodeInfo == false)
			return false;
			
		list($iLeftId, $iRightId) = $dNodeInfo;
		
		$this->db->delete("DELETE FROM `" . $this->treeStructure['Table'] . "` WHERE (`" . $this->treeStructure['Lft'] . "` BETWEEN " . $iLeftId . " AND " . $iRightId . ") " . $sCondition, $dataParams);
		
		$deltaId = (($iRightId - $iLeftId) + 1);
		
		$this->db->update("
			UPDATE `" . $this->treeStructure['Table'] . "` SET 
			`" . $this->treeStructure['Lft'] . "` = (CASE WHEN `" . $this->treeStructure['Lft'] . "` > " . $iLeftId . " THEN `" . $this->treeStructure['Lft'] . "` - " . $deltaId . " ELSE `" . $this->treeStructure['Lft'] . "` END), 
			`" . $this->treeStructure['Rgt'] . "` = (CASE WHEN `" . $this->treeStructure['Rgt'] . "` > " . $iLeftId . " THEN `" . $this->treeStructure['Rgt'] . "` - " . $deltaId . " ELSE `" . $this->treeStructure['Rgt'] . "` END) 
			WHERE `" . $this->treeStructure['Rgt'] . "` > " . $iRightId . $sCondition, $dataParams);
		
		return true;
	}
	
	/**
	 * 初始化树形根节点。
	 *
	 * @param array $data
	 */
	function initialize($data)
	{
		$iCount = $this->db->scalar("SELECT COUNT(*) FROM `" . $this->treeStructure['Table'] . "`");
		if (0 == $iCount) {
			$fields = $values = '';
			$dataParams = array();
			foreach ($data as $k => $v) {
				$fields .= ", `" . $k . "`";
				$values .= ", ?";
				$dataParams[] = $v;
			}
			
			$this->db->insert("INSERT INTO `" . $this->treeStructure['Table'] . "`(`" . $this->treeStructure['Lft'] . "`, `" . $this->treeStructure['Rgt'] . "`, `" . $this->treeStructure['Level'] . "`" . $fields . ") VALUES(1, 2, 0" . $values . ")", $dataParams);
		}
	}
	
	/**
	 * 获取节点信息。
	 *
	 * @param int $sectionId
	 * @return array
	 */
	function getNodeInfo($sectionId)
	{
		$data = $this->db->fetch("SELECT `" . $this->treeStructure['Lft'] . "`, `" . $this->treeStructure['Rgt'] . "`, `" . $this->treeStructure['Level'] . "` FROM `" . $this->treeStructure['Table'] . "` WHERE `" . $this->treeStructure['Primary'] . "` = ?", array(
			( int ) $sectionId
		));
		if (is_array($data)) {
			return array(
				$data[$this->treeStructure['Lft']], 
				$data[$this->treeStructure['Rgt']], 
				$data[$this->treeStructure['Level']]
			);
		} else {
			return false;
		}
	}
	
	/**
	 * 获取父节点信息。
	 *
	 * @param int $sectionId
	 * @param string $condition
	 * @param array $dataParams
	 * @return array
	 */
	function getParentInfo($sectionId, $condition = '', $dataParams = NULL)
	{
		$dNodeInfo = $this->getNodeInfo($section_id);
		
		if ($dNodeInfo == false)
			return false;
			
		list($leftId, $rightId, $level) = $dNodeInfo;
		$level--;
		
		$d = $this->db->fetch("SELECT * FROM `" . $this->treeStructure['Table'] . "` WHERE `" . $this->treeStructure['Lft'] . "` < " . $leftId . " AND `" . $this->treeStructure['Rgt'] . "` > " . $rightId . " AND `" . $this->treeStructure['Level'] . "` = " . $level . $condition . " ORDER BY `" . $this->treeStructure['Lft'] . "`", $dataParams);
		return $d;
	}
	
	/**
	 * 获取整棵树。
	 *
	 * @param boolean $full
	 * @return array
	 */
	function getTree($full = true)
	{
		if ($full) {
			$iSql = "SELECT DISTINCT N.`ClassID`, REPEAT('　', (N.`Depth` - 1) * 2 + 2) AS `ClassNamePrefix`, N.`ClassName`, N.`Lft`, N.`Depth` FROM `ms_classes` P, `ms_classes` N WHERE N.`Lft` BETWEEN P.`Lft` AND P.`Rgt` ORDER BY N.`Lft` ASC";
		} else {
			$iSql = "SELECT DISTINCT N.`ClassID`, REPEAT('　', (N.`Depth` - 1) * 2) AS `ClassNamePrefix`, N.`ClassName`, N.`Lft`, N.`Depth` FROM `ms_classes` P, `ms_classes` N WHERE N.`Depth` > 0 AND N.`Lft` BETWEEN P.`Lft` AND P.`Rgt` ORDER BY N.`Lft` ASC";
		}
		$d = $this->db->fetchAll($iSql);
		return $d;
	}
}